package com.satis.compose.get.navigator

import android.annotation.SuppressLint
import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.AnimatedVisibilityScope
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.tween
import androidx.compose.runtime.Composable
import androidx.navigation.NamedNavArgument
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavDeepLink
import androidx.navigation.NavGraph
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.navigation
import androidx.navigation.get
import com.satis.compose.get.annotation.PageRoute

/**
 * Add the [Composable] to the [NavGraphBuilder]
 *
 * @param route route for the destination
 * @param arguments list of arguments to associate with destination
 * @param deepLinks list of deep links to associate with the destinations
 * @param enterTransition callback to determine the destination's enter transition
 * @param exitTransition callback to determine the destination's exit transition
 * @param popEnterTransition callback to determine the destination's popEnter transition
 * @param popExitTransition callback to determine the destination's popExit transition
 * @param content composable for the destination
 */
@SuppressLint("NewApi") // b/187418647
@ExperimentalAnimationApi
fun NavGraphBuilder.definedPage(
    route: String,
    arguments: List<NamedNavArgument> = emptyList(),
    deepLinks: List<NavDeepLink> = emptyList(),
    enterTransition: (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition?) = PageRoute.Animation.RIGHT_IN.enterTransition,
    exitTransition: (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition?) = PageRoute.Animation.LEFT_OUT.exitTransition,
    popEnterTransition: (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition?) = PageRoute.Animation.LEFT_IN.enterTransition,
    popExitTransition: (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition?) = PageRoute.Animation.RIGHT_OUT.exitTransition,
    content: @Composable AnimatedVisibilityScope.(NavBackStackEntry) -> Unit
) {
    addDestination(GetAnimatedComposeNavigator.Destination(
        provider[GetAnimatedComposeNavigator::class], content
    ).apply {
        this.route = route
        arguments.forEach { (argumentName, argument) ->
            addArgument(argumentName, argument)
        }
        deepLinks.forEach { deepLink ->
            addDeepLink(deepLink)
        }
        enterTransitions[route] = enterTransition
        exitTransitions[route] = exitTransition
        popEnterTransitions[route] = popEnterTransition
        popExitTransitions[route] = popExitTransition
    })
}

@OptIn(ExperimentalAnimationApi::class)
fun NavGraphBuilder.definedPage(
    route: String,
    arguments: List<NamedNavArgument> = emptyList(),
    deepLinks: List<NavDeepLink> = emptyList(),
    enter: PageRoute.Animation = PageRoute.Animation.RIGHT_IN,
    exit: PageRoute.Animation = PageRoute.Animation.LEFT_OUT,
    popEnter: PageRoute.Animation = PageRoute.Animation.LEFT_IN,
    popExit: PageRoute.Animation = PageRoute.Animation.RIGHT_OUT,
    content: @Composable AnimatedVisibilityScope.(NavBackStackEntry) -> Unit
) {
    definedPage(
        route = route,
        arguments = arguments,
        deepLinks = deepLinks,
        enterTransition = enter.enterTransition,
        exitTransition = exit.exitTransition,
        popEnterTransition = popEnter.enterTransition,
        popExitTransition = popExit.exitTransition,
        content
    )
}

@OptIn(ExperimentalAnimationApi::class)
private val PageRoute.Animation.enterTransition: (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition?)
    get() {
        when (this) {
            PageRoute.Animation.LEFT_IN -> {
                return {
                    slideIntoContainer(
                        androidx.compose.animation.AnimatedContentScope.SlideDirection.Left,
                        animationSpec = androidx.compose.animation.core.tween(300),
                    )
                }
            }
            PageRoute.Animation.RIGHT_IN -> {
                return {
                    slideIntoContainer(
                        AnimatedContentScope.SlideDirection.Right,
                        animationSpec = tween(300),
                    )
                }
            }
            PageRoute.Animation.UP_IN -> {
                return {
                    slideIntoContainer(
                        AnimatedContentScope.SlideDirection.Up,
                        animationSpec = tween(300),
                    )
                }
            }

            PageRoute.Animation.DOWN_IN -> {
                return {
                    slideIntoContainer(
                        AnimatedContentScope.SlideDirection.Down,
                        animationSpec = tween(300),
                    )
                }
            }

            else -> {
                return {
                    slideIntoContainer(
                        AnimatedContentScope.SlideDirection.Right,
                        animationSpec = tween(300),
                    )
                }
            }
        }
    }


@OptIn(ExperimentalAnimationApi::class)
private val PageRoute.Animation.exitTransition: (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition?)
    get() {
        when (this) {
            PageRoute.Animation.LEFT_OUT -> {
                return {
                    slideOutOfContainer(
                        AnimatedContentScope.SlideDirection.Left,
                        animationSpec = tween(300),
                    )
                }
            }
            PageRoute.Animation.RIGHT_OUT -> {
                return {
                    slideOutOfContainer(
                        AnimatedContentScope.SlideDirection.Right,
                        animationSpec = tween(300),
                    )
                }
            }

            PageRoute.Animation.UP_OUT -> {
                return {
                    slideOutOfContainer(
                        AnimatedContentScope.SlideDirection.Up,
                        animationSpec = tween(300),
                    )
                }
            }

            PageRoute.Animation.DOWN_OUT -> {
                return {
                    slideOutOfContainer(
                        AnimatedContentScope.SlideDirection.Down,
                        animationSpec = tween(300),
                    )
                }
            }
            else -> {
                return {
                    slideOutOfContainer(
                        AnimatedContentScope.SlideDirection.Left,
                        animationSpec = tween(300),
                    )
                }
            }
        }
    }

/**
 * Construct a nested [NavGraph]
 *
 * @param startDestination the starting destination's route for this NavGraph
 * @param route the destination's unique route
 * @param arguments list of arguments to associate with destination
 * @param deepLinks list of deep links to associate with the destinations
 * @param enterTransition callback to define enter transitions for destination in this NavGraph
 * @param exitTransition callback to define exit transitions for destination in this NavGraph
 * @param popEnterTransition callback to define pop enter transitions for destination in this
 * NavGraph
 * @param popExitTransition callback to define pop exit transitions for destination in this NavGraph
 * @param builder the builder used to construct the graph
 *
 * @return the newly constructed nested NavGraph
 */
@ExperimentalAnimationApi
fun NavGraphBuilder.navigation(
    startDestination: String,
    route: String,
    arguments: List<NamedNavArgument> = emptyList(),
    deepLinks: List<NavDeepLink> = emptyList(),
    enterTransition: (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition?)? = null,
    exitTransition: (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition?)? = null,
    popEnterTransition: (AnimatedContentScope<NavBackStackEntry>.() -> EnterTransition?)? = enterTransition,
    popExitTransition: (AnimatedContentScope<NavBackStackEntry>.() -> ExitTransition?)? = exitTransition,
    builder: NavGraphBuilder.() -> Unit
) {
    navigation(startDestination, route, arguments, deepLinks, builder).apply {
        enterTransition?.let { enterTransitions[route] = enterTransition }
        exitTransition?.let { exitTransitions[route] = exitTransition }
        popEnterTransition?.let { popEnterTransitions[route] = popEnterTransition }
        popExitTransition?.let { popExitTransitions[route] = popExitTransition }
    }
}